package com.zl.learning.excel.util;

import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.time.DateFormatUtils;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.xssf.streaming.SXSSFWorkbook;
import org.apache.poi.xssf.usermodel.XSSFClientAnchor;

import javax.servlet.http.HttpServletResponse;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Field;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Date;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Stream;

import static org.apache.poi.ss.usermodel.Workbook.PICTURE_TYPE_JPEG;

public class ExcelUtil {

    private static DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyyMMddHHmmss");

    public static void exportToExcel(HttpServletResponse response, String name, List<?> data, Class<?> dataClass) throws IOException {
        response.setHeader("content-Type", "application/vnd.ms-excel");
        response.setHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(name + LocalDateTime.now().format(formatter) + ".xls", StandardCharsets.UTF_8));
        response.setCharacterEncoding("UTF-8");
        Workbook workbook = exportExcel(dataClass, data);
        workbook.write(response.getOutputStream());
    }

    public static Workbook exportExcel(Class<?> cl, List<?> dataList) {
        //1.创建Excel对象
        SXSSFWorkbook wb = new SXSSFWorkbook(dataList.size() + 1);//默认值是100
        //2.创建Sheet对象
        Sheet sheet = wb.createSheet();
        //行的索引
        int rowIndex = 0;
        //单元格的索引
        int cellIndex = 0;
        Row nRow = null;
        Cell nCell = null;
        //10.创建小标题行
        nRow = sheet.createRow(rowIndex++);//使用的是1，使用完了再加1
        //9.创建小标题内容
        final Field[] fields = cl.getDeclaredFields();
        Map<Field, Excel> excelMap = new LinkedHashMap<>();
        for (Field field : fields) {
            final Excel excel = field.getAnnotation(Excel.class);
            if (excel != null) {
                excelMap.put(field, excel);
                //12.创建小标题的单元格
                nCell = nRow.createCell(cellIndex++);
                //设置小标题内容
                nCell.setCellValue(excel.name());
            }
        }
        //画图的顶级管理器，一个sheet只能获取一个（一定要注意这点）
        Drawing patriarch = sheet.createDrawingPatriarch();
        //14.遍历数据
        for (Object value : dataList) {
            //15.创建数据行
            nRow = sheet.createRow(rowIndex++);
            cellIndex = -1;
            for (Map.Entry<Field, Excel> fieldExcelEntry : excelMap.entrySet()) {
                nCell = nRow.createCell(++cellIndex);
                Field field = fieldExcelEntry.getKey();
                Excel excel = fieldExcelEntry.getValue();
                field.setAccessible(true);
                try {
                    final Object o = field.get(value);
                    if (o == null) {
                        continue;
                    }
                    final int type = excel.type();
                    switch (type) {
                        case 1: {
                            final String format = excel.format();
                            if (o instanceof Date) {
                                nCell.setCellValue(DateFormatUtils.format((Date) o, format));
                                continue;
                            }
                            String val = String.valueOf(o);
                            final String[] s2 = Stream.of(excel.replace()).map(r -> r.split("_")).filter(r -> r.length > 1).filter(r -> r[1].equals(String.valueOf(o))).findAny().orElse(null);
                            if (s2 != null) {
                                val = s2[0];
                            }
                            final String suffix = excel.suffix();
                            if (StringUtils.isNotBlank(suffix)) {
                                val += suffix;
                            }
                            nCell.setCellValue(val);
                            break;
                        }
                        case 2: {
                            final int imageType = excel.imageType();
                            String source = String.valueOf(o);
                            byte[] bytes = null;
                            switch (imageType) {
                                case 1: {
                                    bytes = getImageBytes(new URL(source));
                                    break;
                                }
                                case 2: {
                                    bytes = getImageBytes(source);
                                    break;
                                }
                                default:
                                    break;
                            }
                            if (bytes != null) {
                                XSSFClientAnchor anchor = new XSSFClientAnchor(0, 0, 255, 255, cellIndex, rowIndex - 1, cellIndex + 1, rowIndex);
                                anchor.setAnchorType(ClientAnchor.AnchorType.DONT_MOVE_AND_RESIZE);
                                patriarch.createPicture(anchor, wb.addPicture(bytes, PICTURE_TYPE_JPEG));
                            }
                            break;
                        }
                        default:
                            break;
                    }
                } catch (IllegalAccessException | MalformedURLException e) {
                    e.printStackTrace();
                }
            }
        }
        return wb;
    }

    private static byte[] getImageBytes(URL url) {
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        try (InputStream in = url.openStream()) {
            byte[] bytes = new byte[1024];
            int len = -1;
            while ((len = in.read(bytes)) != -1) {
                out.write(bytes, 0, len);
            }
            return out.toByteArray();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }

    private static byte[] getImageBytes(String filePath) {
        try {
            return Files.readAllBytes(Paths.get(filePath));
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }
}
